#include "expression.h"
#include <cmath>
#include <regex>
#include <stack>
#include <exception>
//std::map<std::string, std::function<double(double)>> expression::one_paramter_map
//(
	//std::make_pair("+",[](double a, double b) {return a + b;})
	//{"+",[](double a, double b) {return a + b;}},
//);
expression::expression(const std::string& operator_string)
{
	//expression(operator_string);
	if (std::regex_match(operator_string, std::regex("[+-]?(\\d+(.\\d*)?)|(.\\d+)")))
		new(this)expression(std::stod(operator_string));
	else if (operator_string.length() == 1)
		new(this)expression(operator_string[0]);
	else if (operator_string == "-x")
	{
		is_variable.related_to_x = 1;
		operation.one_paramter = [](double x) {return -x;};
	}
	else if (operator_string == "-y")
	{
		is_variable.related_to_y = 1;
		operation.one_paramter = [](double y) {return -y;};
	}
	else if (operator_string == "-z")
	{
		is_variable.related_to_z = 1;
		operation.one_paramter = [](double z) {return -z;};
	}
	else if (operator_string == "exp")
		operation.one_paramter = [](double a) {return std::exp(a);};
	else if (operator_string == "ln")
		operation.one_paramter = [](double a) {return std::log(a);};
	else if (operator_string == "lg")
		operation.one_paramter = [](double a) {return std::log10(a);};
	else if (operator_string == "log")
		operation.two_paramters = [](double a, double b) {return std::log(b) / std::log(a);};
	else if (operator_string == "sin")
		operation.one_paramter = [](double a) {return std::sin(a);};
	else if (operator_string == "cos")
		operation.one_paramter = [](double a) {return std::cos(a);};
	else if (operator_string == "tan" or operator_string == "tg")
		operation.one_paramter = [](double a) {return std::tan(a);};
	else if (operator_string == "cot" or operator_string == "ctg")
		operation.one_paramter = [](double a) {return 1.0 / std::tan(a);};
	else if (operator_string == "sec")
		operation.one_paramter = [](double a) {return 1.0 / std::sin(a);};
	else if (operator_string == "csc")
		operation.one_paramter = [](double a) {return 1.0 / std::cos(a);};
	else
	{
		operation.unresolved = operator_string;
		//throw std::bad_cast::__construct_from_string_literal(("cannot resolve input: " + operator_string).c_str());
		throw std::runtime_error("cannot resolve input: " + operator_string);
	}
}

expression::expression(const char operator_char)
{
	switch (operator_char)
	{
	case 'x':
		is_variable.related_to_x = 1;
		operation.one_paramter = [](double x) {return x;};
		break;
	case 'y':
		is_variable.related_to_y = 1;
		operation.one_paramter = [](double y) {return y;};
		break;
	case 'z':
		is_variable.related_to_z = 1;
		operation.one_paramter = [](double z) {return z;};
		break;
	case '+':
		operation.two_paramters = [](double a, double b) {return a + b;};
		break;
	case '-':
		operation.two_paramters = [](double a, double b) {return a - b;};
		break;
	case '*':
		operation.two_paramters = [](double a, double b) {return a * b;};
		break;
	case '/':
		operation.two_paramters = [](double a, double b) {return a / b;};
		break;
	case '^':
		operation.two_paramters = [](double a, double b) {return std::pow(a, b);};
		break;
	}
}

double expression::calculate(const double x, const double y, const double z)
{
	if (is_variable.related_to_x)
		return operation.one_paramter(x);
	if (is_variable.related_to_y)
		return operation.one_paramter(y);
	if (is_variable.related_to_z)
		return operation.one_paramter(z);
	if (is_variable.is_contant)
		return operation.number;
	if (left == nullptr)
		return std::numeric_limits<double>::quiet_NaN();
	return right ?
		operation.two_paramters(left->calculate(x, y, z), right->calculate(x, y, z)) :
		operation.one_paramter(left->calculate(x, y, z));
}
expression* expression::resolve(const std::string& input_string)
{
	std::function<expression* (std::string::const_iterator, const std::string::const_iterator&, const std::string&)>left_to_right_with_two_parameters
	([](std::string::const_iterator first, const std::string::const_iterator& last, const std::string& operators)
		{
			expression* root = nullptr;
			for (std::string::const_iterator q(first + 1);;++q, first = q)
			{
				if (*q == '+')
				{
					++first;
					++q;
				}
				else
					q += *q == '-';
				for (;q not_eq last and operators.find(*q) == std::string::npos;++q);
				if (q == last)
					break;
				(root ? root->right : root) = new expression(std::string(first, q));
				expression* temp = new expression(*q);
				temp->left = root;
				root = temp;
			}
			(root ? root->right : root) = new expression(std::string(first, last));
			return root;
		}
	);
	//std::stack<std::string::const_iterator> parentheses;
	//std::stack<std::string::const_iterator> functions;
	//std::stack<std::string::const_iterator> left_to_right1;
	//std::stack<std::string::const_iterator> left_to_right2;
	//std::stack<double> numbers;
	//std::smatch numbers;
	//std::regex_match(input_string, numbers, std::regex("[+-]?(\\d+(.\\d*)?)|(.\\d+)"));
	//numbers.begin()->str();
	//for (std::string::const_iterator p(input_string.begin());p not_eq input_string.end();++p)
	//{
		//std::regex number();
	//}
	return left_to_right_with_two_parameters(input_string.cbegin(), input_string.cend(), "+-*/");
}